﻿using System;
using RayDen.RayEngine.Core;
using RayDen.RayEngine.Core.Devices;
using RayDen.RayEngine.Core.Interface;
using RayDen.RayEngine.Core.Threads;
using RayDen.RayEngine.IntersectionEngines;
using RayDen.RayEngine.IntersectionEngines.Cuda;
using RayDen.RayEngine.IntersectionEngines.Optix;
using RayDen.RayEngine.Samplers;

namespace RayDen.RayEngine.Managers
{
    public class DevicesManager : IDisposable
    {
        public bool SingleFilm;
        public IntersectionEngine IntersectionDevice;
        public RenderThread[] RenderThreads;

        private RayEngineScene scene;
        private BaseRenderingEngine engine;

        public DevicesManager(RayEngineScene scene, BaseRenderingEngine engine)
        {
            this.engine = engine;
            this.scene = scene;
        }

        public void Initialize(int renderThreadCount, int width, int height, IFrameManager frameManager)
        {
            this.InitDevices(renderThreadCount, width, height, frameManager);
        }


        public void Start()
        {
            foreach (var renderThread in RenderThreads)
            {
                renderThread.Start();
            }
        }

        public void Stop()
        {
            foreach (var renderThread in RenderThreads)
            {
                renderThread.Stop();
            }
        }

        private void InitDevices(int renderThreadCount, int width, int height, IFrameManager fM)
        {
            engine.LowLatencySampling = false;

            this.IntersectionDevice =
                //new MCOpenClIntersectionDevice(scene, true, 0);
                //new DadeCudaIntersectionDevice(scene, null);
                new OptixIntersectionDevice(scene, engine.LowLatencySampling);
            //new OptixPrimeIntersectionDevice(scene, engine.LowLatencySampling);

            //new CpuBvhIntersectionEngine(scene);
            var renderThreadsCount = Math.Min(engine.MaxThreadCount, renderThreadCount);
            this.RenderThreads = new RenderThread[renderThreadsCount];
            int ht = height / renderThreadsCount;
            var samplingContext = new SamplingContext()
                {
                    LightSampler = new LightSampler(scene),
                    SurfaceSampler = new SurfaceSampler(scene)
                };
            for (int i = 0; i < renderThreadsCount; i++)
            {
                var sampler = engine.CreateSampler(width, height, scene.MaxPathDepth, i, ht * i, ht);
                samplingContext.PrimarySpaceSampler = sampler;
                RenderThreads[i] = engine.CreateRenderThread(i, IntersectionDevice, scene, 
                    SingleFilm ? FilmFactory.Cache :
                    FilmFactory.CreateFilm(width, height, GlobalConfiguration.Instance.AdaptiveFilm)
                    , fM, samplingContext);
            }
        }


        public void Dispose()
        {           
            try
            {
                if (IntersectionDevice.IsRunning)
                {
                    IntersectionDevice.Stop();
                }
            }
            finally
            {
                IntersectionDevice.Dispose();
            }
            for (var index = 0; index < RenderThreads.Length; index++)
            {
                try
                {
                    RenderThreads[index].Stop();
                }
                finally
                {
                    RenderThreads[index] = null;
                }
            }
        }

        internal void Interrupt()
        {
            foreach (var renderThread in RenderThreads)
            {
                renderThread.Stop();
            }

            if (IntersectionDevice.IsRunning)
            {
                //IntersectionDevice.Stop();
            }
        }
    }
}